package org.flashbrighton.as3bots.controllers
{	
	import de.polygonal.motor2.dynamics.contact.Contact;
	
	import flash.display.*;
	import flash.events.Event;
	
	import org.flashbrighton.as3bots.bots.*;
	import org.flashbrighton.as3bots.models.*;
	import org.flashbrighton.as3bots.views.*;
	
	import uk.co.steamboy.utils.DynamicTimer;


	public class GameController extends Sprite
	{		
		private const MAX_SPEED:int = 100000;
		private const MAX_TORQUE:int = 500000;
		
		private var _model:GameModel;
		private var _view:GameView;		
		private var _dynamicTimer:DynamicTimer;
		private var _scorer:CollisionScorer;
		
		public var houseBot:HouseBot;


		public function GameController(model:GameModel, view:GameView):void
		{
			_model = model;
			_view = view;
			init();
		}

		
		private function init():void
		{							
			createBots();
			arrangeBots();
			_view.addViews();
			_view.setUI();
			
			_model.isPlaying = false;
			
			_dynamicTimer = new DynamicTimer(30);
			_dynamicTimer.registerUpdater(update);
			_dynamicTimer.registerRenderer(_view.update);
			_dynamicTimer.start();
		}
		
		
		private function createBots():void
		{
			registerBot(new JaimeBot());
			registerBot(new RichsBot());
			registerBot(new NeilnAlisonBot());
			registerBot(new SebsBot());
			registerBot(new JoesBot());
			registerBot(new NikosBot());				
			
			houseBot = new HouseBot();
			registerBot(houseBot);
			houseBot.chassis.skin.addEventListener(Event.ADDED_TO_STAGE, enableHouseBot);			
		}
		
		
		private function registerBot(bot:BaseBot):void
		{
			var botModel:BotModel;
			var radius:Number = bot.chassis.radius;
			var density:Number = bot.chassis.density;
			var friction:Number = bot.chassis.friction;
			var restitution:Number = bot.chassis.restitution;
			var view:Sprite;
			
			botModel = bot.model;
			botModel.body = _model.physics.addCircle(0, 0, radius, density);
			botModel.body.userData = bot;
			
			// Top-down friction
			botModel.body.linDamping = 0.8;
			botModel.body.angDamping = 0.8;
			
			_model.addBot(bot, bot.chassis.skin);
		}
		
		
		private function enableHouseBot(e:Event):void
		{
			var botSkin:Sprite = e.target as Sprite;
			var hbc:HouseBotController = houseBot.controller as HouseBotController;
			hbc.enableKeys(botSkin.stage);
		}
		
		
		private function arrangeBots():void
		{
			//TODO: Values to be based on arena
			var n:int = 0;
			var	br:Number;
			var	bx:Number;
			var	by:Number;
			var	brad:Number;
			var	bot:BaseBot;			
			var numBots:int = _model.botList.length - 1; //exclude house bot
			
			for each (bot in _model.botList)
			{
				br = (360 / numBots * n);
				brad = br * Math.PI / 180;
				bx = 400 + Math.cos(brad) * (125 * 1.5);
				by = 250 + Math.sin(brad) * (125 * 1.5);
				
				bot.model.body.x = bx;
				bot.model.body.y = by;
				bot.model.body.r = (br - 180) * Math.PI / 180;
								
				n++;
			}
			
			houseBot.model.body.x = 800 - 30;
			houseBot.model.body.y = 500 - 30;
			houseBot.model.body.r = 225 * Math.PI / 180;
		}

		
		/**
		 * The main loop.
		 */
		private function update(event:Event=null):void
		{	
			var c:Contact;
						
			if (!_model.isPlaying) return;
			
			moveBots();
			_model.updateWorld();
			checkScores();
			
			if (_model.physics.world.contactCount < 1) return;
			
			c = _model.physics.world.contactList;
			
			if (!c.body1 || !c.body2) return;
			if (!c.body1.contactList || !c.body2.contactList) return;
			
			_scorer = new CollisionScorer(c.body1.contactList.contact.manifolds[0].c0, c.body1.userData, c.body2.userData);
			_scorer.notifyCollidables();			
		}
		
		
		private function moveBots():void
 		{
			var bot:BaseBot;
			var angle:Number;
			var PI:Number = Math.PI;
			var twoPI:Number = PI * 2;
			
			for each (bot in _model.botList)
			{
				if (bot.health.energy < 0) continue;
				
				bot.controller.run();
				bot.engine.power = bot.controller.getEnginePower();
				bot.steering.power = bot.controller.getSteeringPower();
				
				if (bot.engine.power > 1) bot.engine.power = 1;
				if (bot.steering.power > 1) bot.steering.power = 1;
				if (bot.engine.power < -1) bot.engine.power = -1;
				if (bot.steering.power < -1) bot.steering.power = -1;
				
				angle = bot.model.body.r;
				angle += PI;
				angle %= twoPI;
				if (angle < 0) angle += twoPI;
				angle -= PI;
								
				var forceX:Number = (bot.engine.power * MAX_SPEED) * Math.cos(angle);
				var forceY:Number = (bot.engine.power * MAX_SPEED) * Math.sin(angle);
				
				bot.model.body.applyForce(forceX, forceY);
				bot.model.body.applyTorque(bot.steering.power * MAX_TORQUE);
			}
 		}		
		
		
		private function checkScores():void
		{
			for each (var bot:BaseBot in _model.botList)
			{
				if (bot.health.energy < 0)
				{
					_model.removeBot(bot);
				}
				
				if (_model.botList.length == 2) //includes house bot
				{
					_model.isPlaying = false;
					_model.updateWorld();
					break;
				}
			}
		}
		
	}
}